/*
 * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.matezk.dms.things.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.matezk.dms.common.context.UserContextHolder;
import cn.matezk.dms.things.mapper.DeviceMapper;
import cn.matezk.dms.things.model.entity.DeviceDO;
import cn.matezk.dms.things.model.entity.NotifyConfig;
import cn.matezk.dms.things.model.query.DeviceQuery;
import cn.matezk.dms.things.model.req.DeviceConfigReq;
import cn.matezk.dms.things.model.req.DeviceReq;
import cn.matezk.dms.things.model.resp.*;
import cn.matezk.dms.things.service.DeviceService;
import cn.matezk.dms.system.mapper.UserDeviceMapper;
import cn.matezk.dms.system.mapper.UserMapper;
import cn.matezk.dms.system.model.entity.UserDeviceDO;
import cn.matezk.dms.system.service.DeptService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.iotexplorer.v20190423.IotexplorerClient;
import com.tencentcloudapi.iotexplorer.v20190423.models.CallDeviceActionSyncRequest;
import com.tencentcloudapi.iotexplorer.v20190423.models.CallDeviceActionSyncResponse;
import com.tencentcloudapi.iotexplorer.v20190423.models.ControlDeviceDataRequest;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.resp.PageResp;
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;

import java.util.List;

/**
 * 设备业务实现
 *
 * @author 程道林
 * @since 2024/12/01 22:10
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceServiceImpl extends BaseServiceImpl<DeviceMapper, DeviceDO, DeviceResp, DeviceDetailResp, DeviceQuery, DeviceReq> implements DeviceService {
    @Resource
    private DeptService deptService;
    @Resource
    private UserDeviceMapper userDeviceMapper;
    @Resource
    private UserMapper userMapper;

    @Value("${tencent.endpoint}")
    private String endpoint;
    @Value("${tencent.region}")
    private String region;
    @Value("${tencent.secret-id}")
    private String secretId;
    @Value("${tencent.secret-key}")
    private String secretKey;
    @Value("${tencent.token}")
    private String token;

    @Override
    public PageResp<DeviceResp> page(DeviceQuery query, PageQuery pageQuery) {
        query.setUserId(UserContextHolder.getUserId());
        if (ObjectUtil.isNull(query.getDeptId())) {
            query.setDeptId(UserContextHolder.getContext().getDeptId());
        }
        int dataScope = userMapper.dataScope(UserContextHolder.getUserId());
        query.setDataScope(dataScope);
        IPage<DeviceResp> page = baseMapper.selectPage(new Page<>(pageQuery.getPage(), pageQuery.getSize()), query);
        PageResp<DeviceResp> pageResp = PageResp.build(page, super.getListClass());
        return pageResp;
    }

    @Override
    public DeviceDetailResp get(Long id) {
        DeviceResp device = baseMapper.selectByPrimaryKey(id);
        DeviceDetailResp resp = new DeviceDetailResp();
        BeanUtils.copyProperties(device, resp);
        fill(resp);
        return resp;
    }

    @Override
    protected void beforeAdd(DeviceReq req) {
        DeviceDO device = baseMapper.selectOne(new QueryWrapper<DeviceDO>().eq("device_id", req.getDeviceId()));
        CheckUtils.throwIf(ObjectUtil.isNull(device), "设备[{}]不存在", req.getDeviceId());
        Long userId = userDeviceMapper.selectBindUser(req.getDeviceId());
        CheckUtils.throwIf(ObjectUtil.isNotNull(userId) && ObjectUtil.notEqual(userId, UserContextHolder
            .getUserId()), "设备[{}]已被其他用户绑定", req.getDeviceId());
    }

    @Override
    @Transactional
    public Long add(DeviceReq req) {
        UserDeviceDO userDevice = new UserDeviceDO();
        userDevice.setDeviceId(req.getDeviceId());
        userDevice.setUserId(UserContextHolder.getUserId());
        userDevice.setRole(1);
        userDeviceMapper.bind(userDevice);

        DeviceDO device = baseMapper.selectByDeviceId(req.getDeviceId());
        device.setDeviceId(req.getDeviceId());
        device.setDeptId(req.getDeptId());
        device.setAliasName(req.getAliasName());
        if (ObjectUtil.isNotNull(req.getMode())) {
            if (ObjectUtil.isNull(device.getDesired())) {
                device.setDesired(JSONUtil.createObj());
            }
            device.getDesired().set("mode", req.getMode());
        }
        baseMapper.updateByPrimaryKeySelective(device);
        return 0L;
    }

    @Override
    public void delete(List<Long> ids) {
        ids.forEach(id -> {
            DeviceDO device = baseMapper.selectByPrimaryKey(id);
            if (ObjectUtil.isNotNull(device)) {
                userDeviceMapper.unbind(device.getDeviceId());
                baseMapper.unbind(device.getDeviceId());
            }
        });
    }

    @Override
    protected void beforeUpdate(DeviceReq req, Long id) {
        Long userId = userDeviceMapper.selectBindUser(req.getDeviceId());
        CheckUtils.throwIf(ObjectUtil.isNotNull(userId), "设备[{}]已被其他用户绑定", req.getDeviceId());
        super.beforeUpdate(req, id);
    }

    @Override
    public void update(DeviceReq req, Long id) {
        DeviceResp device = baseMapper.selectByPrimaryKey(id);
        if ("ERNEJU7P1K".equals(req.getProductId()) && ObjectUtil.isNotNull(req.getMode())) {
            if (ObjectUtil.isNull(device.getDesired()))
                device.setDesired(JSONUtil.createObj());
            device.getDesired().set("mode", req.getMode());
            if (ObjectUtil.notEqual(req.getMode(), device.getDesiredMode())) {
                JSONObject params = JSONUtil.createObj().set("mode", Integer.valueOf(req.getMode()));
                try {
                    control(device.getProductId(), device.getDeviceName(), params.toString());
                } catch (TencentCloudSDKException e) {
                    log.error("设置工作模式失败:{}", e.getMessage(), e);
                } catch (Exception e) {
                    log.error("设置工作模式失败:{}", e.getMessage(), e);
                }
            }
        }
        device.setAliasName(req.getAliasName());
        device.setDeptId(req.getDeptId());
        baseMapper.updateByPrimaryKeySelective(device);
    }

    @Override
    public void config(DeviceConfigReq configReq) {
        // 如果是单个设备 直接下发
        if (configReq.getDeviceIds().size() == 1) {
            DeviceDO device = baseMapper.selectByPrimaryKey(configReq.getDeviceIds().get(0));
            JSONObject desired = JSONUtil.createObj();
            if (ObjectUtil.isNotNull(device.getDesiredMode()) && !device.getDesiredMode()
                .equals(device.getReportedMode())) {
                desired.set("mode", Integer.valueOf(device.getDesiredMode()));
            }
            if (ObjectUtil.isNotNull(device.getDesiredFid()) && !device.getDesiredFid()
                .equals(device.getReportedFid())) {
                desired.set("fid", device.getDesiredFid());
            }
            if (ObjectUtil.isNotNull(device.getDesiredStandby()) && !device.getDesiredStandby()
                .equals(device.getReportedStandby())) {
                desired.set("standby", device.getDesiredStandby());
            }
            if (ObjectUtil.isNotNull(device.getDesiredGuard()) && !device.getDesiredGuard()
                .equals(device.getReportedGuard())) {
                desired.set("guard", device.getDesiredGuard());
            }
            if (!desired.isEmpty()) {
                try {
                    control(device.getProductId(), device.getDeviceName(), desired.toString());
                } catch (TencentCloudSDKException e) {
                    log.error(e.getMessage());
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
        }
        // 保存到数据库
        configReq.getDeviceIds().forEach(id -> {
            DeviceResp device = baseMapper.selectByPrimaryKey(id);
            if (ObjectUtil.isNull(device.getDesired()))
                device.setDesired(JSONUtil.createObj());
            if (ObjectUtil.isNotNull(configReq.getFid()))
                device.getDesired().set("fid", configReq.getFid());
            if (ObjectUtil.isNotNull(configReq.getStandby()))
                device.getDesired().set("standby", configReq.getStandby());
            if (ObjectUtil.isNotNull(configReq.getGuard()))
                device.getDesired().set("guard", configReq.getGuard());
            baseMapper.updateByPrimaryKeySelective(device);
        });

    }

    @Override
    public Long countByDeptIds(List<Long> deptIds) {
        return baseMapper.lambdaQuery().in(DeviceDO::getDeptId, deptIds).count();
    }

    @Override
    public void control(String productId, String deviceName, String data) throws TencentCloudSDKException {
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(endpoint);
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        Credential credential = new Credential(secretId, secretKey);
        ControlDeviceDataRequest request = new ControlDeviceDataRequest();
        request.setProductId(productId);
        request.setDeviceName(deviceName);
        request.setData(data);
        IotexplorerClient client = new IotexplorerClient(credential, region, clientProfile);
        client.ControlDeviceData(request);
    }

    @Override
    public CallDeviceActionSyncResponse callAction(CallDeviceActionSyncRequest request) throws TencentCloudSDKException {
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(endpoint);
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        Credential credential = new Credential(secretId, secretKey);
        IotexplorerClient client = new IotexplorerClient(credential, region, clientProfile);
        return client.CallDeviceActionSync(request);
    }

    @Override
    public List<PropertyResp> property(List<String> ids) {
        return baseMapper.selectPropertyBatch(ids);
    }

    @Override
    public SaleStatResp saleStat() {
        return baseMapper.saleStat();
    }

    @Override
    public HotSalesResp hotSales() {
        return baseMapper.hotSales();
    }

    @Override
    public MonthSaleResp monthSale() {
        return baseMapper.monthSale();
    }

    @Override
    public void notifyConfig(Long id, NotifyConfig notifyConfig) {
        DeviceDO device = new DeviceDO();
        device.setId(id);
        device.setNotifyConfig(notifyConfig);
        baseMapper.updateByPrimaryKeySelective(device);
    }
}